"""
This is scrapbook for me to perform performance testing.

The target is a A a subclass of list v.s. B a Sequence class delegate into self.data.

Test are done with
Python 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit (Intel)] on win32


Construction time are the same

In [28]: timeit a1 = A(xrange(1000000))
10 loops, best of 3: 23.3 ms per loop

In [29]: timeit b1 = B(xrange(1000000))
10 loops, best of 3: 23.4 ms per loop

See rest of the tests below.

"""
import collections
import itertools
import operator

class A(list):
    pass

class B(collections.Sequence):
    def __init__(self, iterable):
        self.data = list(iterable)
        #2012-01-15 assign a bounded method of self.data to self does not seems to work!?
        #self.__getitem__ = self.data.__getitem__
    def __len__(self):
        return len(self.data)
    def __iter__(self):
        return iter(self.data)
    def __getitem__(self, key):
        return self.data[key]
    def __eq__(self,other):
        return self.data == other
# 2012-01-15 These are no good. It gets 116ms v.s. baseline of 30ms.
#        return (
#            len(self) == len(other) and
#            all(x==y for x,y in itertools.izip(self,other))
#            all(itertools.imap(operator.eq, self, other))
#            )

a1 = A(xrange(1000000))
a2 = A(xrange(1000000))
b1 = B(xrange(1000000))
b2 = B(xrange(1000000))



#iter is about the same as list
#
#In [78]: timeit try_iter(a1)
#10 loops, best of 3: 69.7 ms per loop
#
#In [79]: timeit try_iter(b1)
#10 loops, best of 3: 69.2 ms per loop

def try_iter(s):
    return [x for x in s]


#__getitem__ suffers 100% overhead
#
#In [74]: timeit try_getitem(a1)
#1 loops, best of 3: 288 ms per loop
#
#In [75]: timeit try_getitem(b1)
#1 loops, best of 3: 565 ms per loop

def try_getitem(s):
    return [s[i] for i in xrange(len(s))]



# Try catch has small overhead. But it beats isinstance checking and type checking
#
# In [14]: timeit [try_custom_getitem_baseline(a1,i) for i in xrange(len(a1))]
# 1 loops, best of 3: 522 ms per loop
#
# In [16]: timeit [try_custom_getitem_isinstance(a1,i) for i in xrange(len(a1))]
# 1 loops, best of 3: 703 ms per loop
#
# In [6]: timeit [try_custom_getitem_type(a1,i) for i in xrange(len(a1))]
# 1 loops, best of 3: 757 ms per loop
#
# In [19]: timeit [try_custom_getitem_trycatch(a1,i) for i in xrange(len(a1))]
# 1 loops, best of 3: 542 ms per loop
#
# In [16]: timeit [try_custom_getitem_addition_trick(a1,i) for i in xrange(len(a1))]
# 1 loops, best of 3: 550 ms per loop

def try_custom_getitem_baseline(s, key):
    return s[key]

def try_custom_getitem_isinstance(s, key):
    if isinstance(key,int):
        return s[key]

def try_custom_getitem_type(s, key):
    if type(key) == int:
        return s[key]

def try_custom_getitem_trycatch(s, key):
    try:
        return s[key]
    except:
        return None

def try_custom_getitem_addition_trick(s, key):
    try:
        # key can be int or slice. This is a fast trick to distinguish it.
        return s[key+0]
    except:
        return None





# Building a list with one list() v.s. list.extend() has similar result
#
# In [28]: l=range(10*1000*1000)      # 10M
#
# In [29]: timeit list(l)
# 10 loops, best of 3: 149 ms per loop
#
# In [30]: timeit ll=[1]; ll.extend(l)
# 10 loops, best of 3: 144 ms per loop